#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_pm.h"
#include "nvs_flash.h"


#include <string.h>
#include <sys/param.h>
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_netif.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "driver/gpio.h"

static const char *payload = "Message from ESP32 ";
#define DEFAULT_SSID "AIOT@FAE"
#define DEFAULT_PWD "fae12345678"
#define DEFAULT_LISTEN_INTERVAL 100
static const char *TAG = "power_save";
#define TCP_IP "192.168.31.226"
#define TCP_PORT 3388
#define blink_gpio 13
bool wifi_connot=0;
bool haveRX=0;
esp_timer_handle_t sleep_timer;
uint8_t power_mode;

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip: " IPSTR, IP2STR(&event->ip_info.ip));
        wifi_connot=1;
    }
}

/*init wifi as sta and set power save mode*/
static void wifi_power_save(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = DEFAULT_SSID,
            .password = DEFAULT_PWD,
            .listen_interval = DEFAULT_LISTEN_INTERVAL,  //listen_interval=100 即100个Beacon-Interval，每个Beacon-Interval默认为100ms
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "esp_wifi_set_ps().");
    esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
}

static void tcp_client_task(void *pvParameters)
{
    char rx_buffer[1024];
    char host_ip[] = TCP_IP;
    int addr_family = 0;
    int ip_protocol = 0;

    while(!wifi_connot)
    {
        vTaskDelay(200 / portTICK_PERIOD_MS);
    }

    while (1) {
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(host_ip);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(TCP_PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;

        int sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, TCP_PORT);

        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
        if (err != 0) {
            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Successfully connected");

        while (1) {
            int err = send(sock, payload, strlen(payload), 0);
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }

            int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "recv failed: errno %d", errno);
                break;
            }
            // Data received
            else {
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
                haveRX=1;
                esp_timer_stop(sleep_timer);
                esp_timer_start_once(sleep_timer,3000000);
                if(power_mode==WIFI_PS_MAX_MODEM)
                {
                    esp_wifi_set_ps(WIFI_PS_NONE);
                    power_mode=WIFI_PS_NONE;
                }
            }

            vTaskDelay(20 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

static void sleep_timer_cb(void* arg)
{
    haveRX=0;
}

static void power_task(void *pvParameters)
{
    while(1)
    {
        if(haveRX==0)
        {
            if(power_mode==WIFI_PS_NONE)
            {
                power_mode=WIFI_PS_MAX_MODEM;
                esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
            }
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void gpio_tesk()
{
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask =  (1ULL<<blink_gpio) ;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);    
    gpio_set_level(blink_gpio, 1);
    ESP_ERROR_CHECK(gpio_hold_en(blink_gpio));	   
}

void app_main(void)
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK( ret );

    const esp_timer_create_args_t sleep_timer_args = {
    .callback = &sleep_timer_cb,
    .name = "sleep_timer"
    };
    
    gpio_tesk();

    // esp_timer_handle_t blink_timer;
    ESP_ERROR_CHECK(esp_timer_create(&sleep_timer_args, &sleep_timer));  
    // ESP_ERROR_CHECK(esp_timer_start_periodic(blink_timer, 5000000));   
    // Configure dynamic frequency scaling:
    // maximum and minimum frequencies are set in sdkconfig,
    // automatic light sleep is enabled if tickless idle support is enabled.
    esp_pm_config_esp32_t pm_config = {
            .max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,  //80M
            .min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,  //10M
            .light_sleep_enable = true
    };
    ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );

    wifi_power_save();

    xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
    xTaskCreate(power_task, "power_task", 2048, NULL, 5, NULL);
}
